package top.jfunc.token.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * API接口token管理器主要逻辑实现【支持多设备登录、踢人】
 * @author xiongshiyan at 2018/8/15 , contact me with email yanshixiong@126.com or phone 15208384257
 */
public abstract class BaseMultiTokenManager<M> extends AbstractRedisTokenManager<M> {
    private static final Logger logger = LoggerFactory.getLogger(BaseMultiTokenManager.class);

    /**
     * 多设备登录 ?
     */
    private boolean multi = false;

    @Override
    public String createToken(M m) {
        String token = getToken(m);
        saveToken(token, m);
        logger.info("createToken token = {} , m={}" , token , m.toString());
        return token;
    }

    @Override
    public void saveToken(String token, M m) {
        if(null == token || null == m){
            return;
        }
        redisTemplate.opsForValue().set(token, m, apiExpires, TimeUnit.SECONDS);
        if(!multi){
            redisTemplate.opsForValue().set(key(m), token, apiExpires, TimeUnit.SECONDS);
        }
    }

    @Override
    public void updateExpires(String token){
        if(null == token){
            return;
        }
        redisTemplate.expire(token, apiExpires, TimeUnit.SECONDS);
        if(multi){
            return;
        }

        //单设备需要额外同步更新
        M m = findByToken(token);
        if(null != m){
            redisTemplate.expire(key(m), apiExpires, TimeUnit.SECONDS);
        }
    }

    @Override
    public boolean deleteToken(String token) {
        //单设备需要额外删除
        M m = findByToken(token);

        boolean b = super.deleteToken(token);
        if(multi){
            return b;
        }

        String key = key(m);
        if(null != m){
            redisTemplate.delete(key);
        }
        logger.info("deleteToken token = {} , key={}" , token , key);
        return b;
    }

    @Override
    public void kickingOld(M m, String newToken) {
        if(multi){
            throw new IllegalStateException("多设备登录情况不允许踢人");
        }
        //1.删除以前登录人的token，以前的人就通不过校验
        String key = key(m);
        String oldToken = (String) redisTemplate.opsForValue().get(key);
        if(null != oldToken){
            super.deleteToken(oldToken);
        }

        //2.重新建立实体和新token的联系
        //单设备需要额外保存标识和token的关系
        logger.info("kickingOld key={} , value={}" , key , newToken);
        redisTemplate.opsForValue().set(key, newToken , apiExpires, TimeUnit.SECONDS);
    }

    /**
     * 根据实体或者标识获取key
     * @param m 实体或者标识
     * @return 返回保存标识和token关系的key
     */
    abstract protected String key(M m);

    public boolean isMulti() {
        return multi;
    }

    public void setMulti(boolean multi) {
        this.multi = multi;
    }
}
